{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/pinecone-io/examples/blob/master/learn/search/faiss-ebook/locality-sensitive-hashing-random-projection/random_projection_simp.ipynb) [![Open nbviewer](https://raw.githubusercontent.com/pinecone-io/examples/master/assets/nbviewer-shield.svg)](https://nbviewer.org/github/pinecone-io/examples/blob/master/learn/search/faiss-ebook/locality-sensitive-hashing-random-projection/random_projection_simp.ipynb)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Simple Random Projection\n",
    "\n",
    "In this notebook we will demonstrate a few aspects to LSH with random projection.\n",
    "\n",
    "First, we will create our normal vectors."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "nbits = 4  # number of hyperplanes and binary vals to produce\n",
    "d = 2  # vector dimensions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[-0.26623211,  0.34055181],\n",
       "       [ 0.3388499 , -0.33368453],\n",
       "       [ 0.34768572, -0.37184437],\n",
       "       [-0.11170635, -0.0242341 ]])"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import numpy as np\n",
    "# create a set of 4 hyperplanes, with 2 dimensions\n",
    "plane_norms = np.random.rand(nbits, d) - .5\n",
    "plane_norms"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "These normal vectors will be used to return +ve/-ve dot product values for different vectors - producing our hashed binary vectors. Let's visualize the direction of these normal vectors first."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXwAAAD4CAYAAADvsV2wAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8QVMy6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAr1klEQVR4nO3dd3hU1dbH8e/KJCGhk5DQQ5HeSygSwIKFaqiCSpVmQVCv0m0XKWJ5AUEUqQKCXKUFKRcQpEOCSO8gEGroJRBS9vvHxGuABEJmkpNk1ud55smcOXvmrC3y42TvM/uIMQallFKZn5vVBSillEobGvhKKeUiNPCVUspFaOArpZSL0MBXSikX4W51AQ+SN29eU6xYMavLUEqpDGPbtm0XjDF+ie1L14FfrFgxwsLCrC5DKaUyDBE5ntQ+HdJRSikXoYGvlFIuQgNfKaVchAa+Ukq5CA18pZRyERr4SinlIjTwlVLKRWTKwB+76hC7wq9aXYZSSqUrmS7wr0TeYfbWE7SasIFvfz9CXJyu96+UUpAJAz93Vk+W9q1Pw7L5GLl0Px0mb+Hs1dtWl6WUUpbLdIEP9tCf0KE6I1tVYvuJKzQas5ble85aXZZSSlkqUwY+gIjQvlYAi/vUo3Aeb3rN2MbAebuIvBNjdWlKKWWJTBv4f3vMLzvzXg+iV4MSzN56gmZfr2f3KZ3QVUq5nkwf+ACe7m4MbFKOWd1rczMqhpbfbOD7tUd1Qlcp5VJcIvD/FlQyL8v6NuCpMv4MW7KPzlO3cv6aTugqpVyDSwU+QJ5snnzXsQbDWlYk9K9LPD96LSv2nrO6LKWUSnVOCXwRaSQiB0TksIgMSGR/sIjsFJE/RSRMROo547gpJSK8Ursoi9+qR4Fc3vT4IYwhC3Zx606slWUppVSqcjjwRcQGjAcaA+WBl0Sk/D3NVgFVjDFVgVeBSY4e1xlK+udg/pt16V6vODM3n6D5uPXsPX3N6rKUUipVOOMMvxZw2Bhz1BhzB5gDBCdsYIy5YYz5e4Y0G5BuZkuzuNsY0qw8P7xai6u3omkxfgOT1umErlIq83FG4BcCTibYDo9/7S4i0lJE9gO/Yj/LT5SI9Iwf9gmLiIhwQnnJ06C0H8v61qd+qbx8+us+ukwL5fx1ndBVSmUezgh8SeS1+06PjTHzjTFlgRbA0KQ+zBgz0RgTaIwJ9PNL9MbrqcY3exYmdQ5kaHAFthy9SOPR6/htv07oKqUyB2cEfjhQJMF2YeB0Uo2NMWuBx0QkrxOO7XQiQsfHixHyVj38cmTh1WlhfLRwN7ejdUJXKZWxOSPwQ4FSIlJcRDyB9sCihA1EpKSISPzz6oAncNEJx041pfPlYMGbQXQNKsb0TccJHreB/Wd1QlcplXE5HPjGmBigN7Ac2AfMNcbsEZHXROS1+Gatgd0i8if2K3raJZjETbe8PGx81LwC07rW5OLNKF4Yt4FpG46RAUpXSqn7SHoOr8DAQBMWFmZ1GQBEXI+i3887WH0ggqfK+PF52yrkzZ7F6rKUUuouIrLNGBOY2D6X+6ZtSvnlyMKULjX5uHl5Nhy5SKPRa1l94LzVZSmlVLJp4D8CEaFLUHEW9Q7CN1sWuk4N5ZOQPTqhq5TKEDTwU6Bs/pws7B1E58eLMnXDX7QYv4GD565bXZZSSj2QBn4KeXnY+CS4IlO6BBJxPYrmX69nxqa/dEJXKZVuaeA76Omy+Vj6dn3qlPDlg4V76PFDGBdvRFldllJK3UcD3wn8c3gxtUtNPmhWnrUHL9BozDrWHky7ZSGUUio5NPCdxM1N6FavOAveDCK3twedpmzl08V7iYrRCV2lVPqgge9k5QvmZFHvenSsU5RJ64/RcvxGDp/XCV2llPU08FOBt6eNoS0q8n2nQM5cvUWzr9cza8txndBVSllKAz8VPVs+H8vfbkDNYj4Mnr+bnjO2cenmHavLUkq5KA38VOaf04vpXWsxpGk51hw4T6PRa9lw+ILVZSmlXJAGfhpwcxO61y/B/DeCyOHlTofJWxixZB93YuKsLk0p5UI08NNQxUK5WPxWfV6qFcB3a4/SasIGjkTcsLospZSL0MBPY96eNoa3rMR3HWsQfvkWzcauZ87WEzqhq5RKdRr4Fnm+Qn6W9W1AtYDcDJi3i9dn/sGVSJ3QVUqlHg18C+XP5cXMbrUZ2LgsK/edo9HodWw8ohO6SqnUoYFvMTc3odcTjzH/jSCyetp4ZdIWPlu2Xyd0lVJOp4GfTlQqnIvFferRLrAIE9Ycoc23Gzl24abVZSmlMhEN/HQkq6c7I1tXZsIr1Tl+MZKmY9cxN/SkTugqpZzCKYEvIo1E5ICIHBaRAYnsf0VEdsY/NopIFWccN7NqXKkAS/vWp3LhXPT7ZSe9f9zO1choq8tSSmVwDge+iNiA8UBjoDzwkoiUv6fZMeAJY0xlYCgw0dHjZnYFc3szq3sd+jUqw/I9Z2k8Zi1bjl60uiylVAbmjDP8WsBhY8xRY8wdYA4QnLCBMWajMeZy/OZmoLATjpvp2dyEN54syS+v18XT3Y3232/m8+X7iY7VCV2l1KNzRuAXAk4m2A6Pfy0p3YClSe0UkZ4iEiYiYRERehMRgCpFcvNrn/q0qV6Y8auP0ObbTRy/qBO6SqlH44zAl0ReS3SWUUSewh74/ZP6MGPMRGNMoDEm0M/PzwnlZQ7ZsrjzedsqjHu5GkcjbtBkzDp+3hauE7pKqWRzRuCHA0USbBcGTt/bSEQqA5OAYGOMDkanULPKBVn2dgMqFMzFe//ZQZ85f3L1lk7oKqUezhmBHwqUEpHiIuIJtAcWJWwgIgHAPKCjMeagE47p0grl9mZ2zzq891xpluw6Q5Mx6wj965LVZSml0jmHA98YEwP0BpYD+4C5xpg9IvKaiLwW3+xDwBf4RkT+FJEwR4/r6mxuQu+nS/Gf1x7H5ia0+24TX604SIxO6CqlkiDpeQw4MDDQhIXpvw0Pc/12NB8t2sO8P05RPSA3Y9pXo4hPVqvLUkpZQES2GWMCE9un37TNBHJ4efDVi1UZ074qh87doPGYdczfHm51WUqpdEYDPxMJrlqIJX3rUzZ/Dt75aQdvz9nOtds6oauUstPAz2SK+GRlTs86vPNMaUJ22id0tx3XCV2llAZ+puRuc6PvM6WY26sOAC9+t5kxKw/phK5SLk4DPxOrUdSHJX3r07xyAf5v5UHaT9zMyUuRVpellLKIBn4ml9PLg9Htq/F/7aqw/+x1moxZx6Id930vTinlAjTwXUTLaoVZ0qc+pfJlp8/s7bw7909uRMVYXZZSKg1p4LuQAN+szO31OH0almLB9lM0GbOO7ScuP/yNSqlMQQPfxbjb3Hj32dL81OtxYuMMbb7dxLjfDhEbl36/gKeUcg4NfBdVs5h9QrdJpQJ88d+DvPT9Zk5duWV1WUqpVKSB78JyeXswtn1VvmxbhT2nrtJ49FoW79QJXaUyKw18FycitK5RmCV961PcLzu9f9zO+//ZwU2d0FUq09HAVwAU9c3Gz689Tu+nSvLzH+E0HbuOHSevWF2WUsqJNPDV/3jY3Hjv+TLM6VGHOzFxtJ6wkW/WHNYJXaUyCQ18dZ/aJXxZ2rcBz1fIz6hlB3hl0mbOXNUJXaUyOg18lahcWT0Y93I1RrWpzM7wqzQavY6lu85YXZZSygEa+CpJIsKLgUX4tU99ivpm5fVZfzDgl51E3tEJXaUyIg189VDF82bj59fq8vqTj/FT2EmajV3PrvCrVpellHpEGvgqWTzd3ejfqCyzutcm8k4srSZs4NvfjxCnE7pKZRhOCXwRaSQiB0TksIgMSGR/WRHZJCJRIvKeM46prFH3sbwse7s+DcvmY+TS/XScsoWzV29bXZZSKhkcDnwRsQHjgcZAeeAlESl/T7NLQB/gC0ePp6yXO6snEzpUZ2SrSvxx/AqNxqxl+Z6zVpellHoIZ5zh1wIOG2OOGmPuAHOA4IQNjDHnjTGhgN5gNZMQEdrXCmBxn3oUzuNNrxnbGDhvl07oKpWOOSPwCwEnE2yHx7+WIiLSU0TCRCQsIiLC4eJU6nrMLzvzXg+iV4MSzN56guZfr2f3KZ3QVSo9ckbgSyKvpXgmzxgz0RgTaIwJ9PPzc6AslVY83d0Y2KQcM7vV5vrtGFp+s4Hv1x7VCV2l0hlnBH44UCTBdmFAl1x0QfVK5WXZ2w14sow/w5bso/PUrZy/phO6SqUXzgj8UKCUiBQXEU+gPbDICZ+rMiCfbJ5M7FiDYS0rEvrXJRqNWcfKveesLksphRMC3xgTA/QGlgP7gLnGmD0i8pqIvAYgIvlFJBx4FxgiIuEiktPRY6v0SUR4pXZRFr9Vj/w5vej+QxgfLNjN7ehYq0tTyqWJMel3nDUwMNCEhYVZXYZyQFRMLJ8vO8Ck9cco5Z+dsS9Vo1wB/bdeqdQiItuMMYGJ7dNv2qpUlcXdxpBm5fnh1VpcuRVN8LgNTF5/TCd0lbKABr5KEw1K+7Gsb33ql8rL0MV76TotlIjrUVaXpZRL0cBXacY3exYmdQ5kaHAFNh+9SKPRa/ltv07oKpVWNPBVmhIROj5ejJC36uGXIwuvTgvj40V7dEJXqTSgga8sUTpfDha8GUTXoGJM2/gXweM2cODsdavLUipT08BXlvHysPFR8wpM61qTizejaD5uPdM3/kV6vnJMqYxMA19Z7sky/izt24Cgx3z5aNEeuk0P48INndBVytk08FW64JcjC1O61OTj5uVZf/gCjUavY82B81aXpVSmooGv0g0RoUtQcRb1DsI3myddpoby75C9OqGrlJNo4Kt0p2z+nCzsHUTnx4syZcMxWozfwKFzOqGrlKM08FW65OVh45PgikzpEkjE9Siafb2eGZuP64SuUg7QwFfp2tNl87H07frUKeHLBwt20+OHbVzUCV2lUkQDX6V7/jm8mNqlJh80K8/agxE0GrOOdYf0bmhKPSoNfJUhuLkJ3eoVZ8GbQeT29qDj5K0M+3UvUTE6oatUcmngqwylfMGcLOpdj451ivL9umO0HL+Rw+dvWF2WUhmCBr7KcLw9bQxtUZHvOwVy5uotmn29jllbdEJXqYfRwFcZ1rPl87H87QbULObD4Pm76TVjG5dv3rG6LKXSLQ18laH55/RietdaDGlajtUHztNozFo2HL5gdVlKpUsa+CrDc3MTutcvwfw3gsiexZ0Ok7cwYsk+7sTEWV2aUumKUwJfRBqJyAEROSwiAxLZLyIyNn7/ThGp7ozjKpVQxUK5WPxWfV6qFcB3a4/SesJGjkbohK5Sf3M48EXEBowHGgPlgZdEpPw9zRoDpeIfPYEJjh5XqcR4e9oY3rIS33WswcnLkTQdu54Zq1YSExlpdWlKWc7dCZ9RCzhsjDkKICJzgGBgb4I2wcAPxn4ZxWYRyS0iBYwxZ5xwfOXizJ07RJ89S/Tp00SfOk306dNUOnWK/5wIJ+LwUXL8colptdx57oOvCHjsWavLVcoyzgj8QsDJBNvhQO1ktCkE3Bf4ItIT+28BBAQEOKE8ldHF3bxpD/OEj1P/PI+JiICEl2SK4O7nh0fBghSuG0jY1R1sKBrBN+veoevWALo/9zXevo9Z1yGlLOKMwJdEXrv3gujktLG/aMxEYCJAYGCgXlidyRljiL1y5Z/wTiTUY69cuftNHh545M+PR8GCZAsKwqNgQfujUPzP/PkRT8//NQ8A6l86xJcr+jDx9kkWL2hOP/96PP3M54h3rjTtr1JWckbghwNFEmwXBk6noI3KhExcHDERF4g+feqeM/S/t89g7hlfF2/v/4W4V6VK9wW6u58fYrM9Uh1+PqUY2W4pbQ7/yrBNn/D2pU0EzajDwPKvUvTxt8Hm4cReK5U+iaPfThQRd+Ag0BA4BYQCLxtj9iRo0xToDTTBPtwz1hhT62GfHRgYaMLCwhyqT6UuEx1N9Llz9rPxU6fuG3qJOXMGEx1913vccuW6+4z8f49CeBQqiC13bkQS+6XQOaLjopmz5Qu+OTCbKOLocsdG97ofkrVCK0jF4yqVFkRkmzEmMNF9zvg6uog0AUYDNmCKMWaYiLwGYIz5Vux/e8cBjYBIoKsx5qFJroFvvbhbtxIdN/9foJ8/D3F3X+/+9/j5/87I7wl1W/ZsFvXmbhciI/hq9XuEXPiD/DEx9Bc/Gj7zBRLw0HMRpdKtVA/81KKBnzZiIiK4tXNnooEee+nS3Y3d3fHIl+/+cfNChezhXqAAbgnGzzOCbWe2Muz3/hyKukDdyFsMzFODYs+NAJ8SVpem1CPTwFcPdDVkMafffx8A8fK6Z5jl7qEXd3//Rx4/zwhi4mL4ac90xm0fz+24O3S+dpOeJVuT9clBkNXH6vKUSjYNfPVAMZcvEx0ejkfBgth8fFJ1/Dy9u3DrAv+3eTiLTqwgX0wM/a5F8WzgW0id18HDy+rylHooDXylHtH289sZtv4DDlw/Tp1btxgY5UWJpz6Eim3ATZegUunXgwJf/89VKhHV/Ksxp8UCBtYayJ7sPrTOJXy1+n0iv38Cjq21ujylUkQDX6kkuLu583K5lwlpvZRmJYOZmjsnzT0us+zndpiZbeH8fqtLVOqRaOAr9RC+3r4MrfcpMxrPwCdvGd73z0uPyF0cnVQfFvWB62etLlGpZNHAVyqZqvpXZU6zuQyuPZi92fPQulABvvwrhJtfV4fVIyBKl2JW6ZsGvlKPwOZmo33Z9ixu9SvNS7ZgWq7svFAoP0vDxmK+rg5hUyE2xuoylUqUBr5SKeDj5cO/g/7NzCYz8fUpST//vHT3zcHh5e/Dt0FwcPndK3gqlQ5o4CvlgCp+VZjddDZDag9hv5cXbYsU4guPW9yc3Q6mN4fT260uUan/0cBXykE2NxvtyrZjccvFBJdsyQ9ZoHnJMiy5egAz8Un4pQdcOWF1mUpp4CvlLHm88vBx3Y+Z1WQW/rmK0z+3F6+Wqc6hw7/C14Hw3w/g1hWry1QuTANfKSer5FeJWU1m8UGdDzgk0bQt4MeoktW4sWkcjK0Km76BmDtWl6lckAa+UqnA5mbjxTIvsrjFYlqWasXMO2doXroCIflLYJYPhPE1Yc98ndhVaUoDX6lUlNsrNx89/hE/Nv2R/DmKMIgIulR5koOeWeA/XWDys3Bis9VlKhehga9UGqiYtyKzms7io8c/4mjUJV7MdofPAlty/epJmPI8zHkFLhy2ukyVyWngK5VG3MSNNqXbENIihNalWjPr4h80L5yfRTXbY46ugW9qw6/vwc0LVpeqMildHlkpi+y5sIdhW4ax68IuqvtWZFBMNsrsmAceWaHe21DnDfDManWZKoNJteWRRcRHRFaIyKH4n3mSaDdFRM6LyG5HjqdUZlIhbwVmNpnJJ3U/4diNU7x4LZQRT/TgWrG68NtQGBcI22dBXKzVpapMwtEhnQHAKmNMKWBV/HZipmG/gblSKgE3caNVqVaEtAyhbem2zD6+lOa2cyxsNIS47P6w8A347gk48pvVpapMwNHADwamxz+fDrRIrJExZi1wKbF9SinIlSUXQ+oMYU6zORTOUZghB36gc+Ei7G88DKKuwoyWMKMVnNVfklXKOTSGLyJXjDG5E2xfNsYkNaxTDFhsjKn4kM/sCfQECAgIqHH8+PEU16dURhRn4lh4eCGj/xjNlagrtCvVht4xWcm5YSzcvgpVX4GnB0POglaXqtIhh+5pKyIrgfyJ7BoMTHd24Cekk7bKlV2Nusq47eOYe3AuubPk5u2KPQg+uRe30O9BbPD4mxDUF7xyWl2qSkccmrQ1xjxjjKmYyGMhcE5ECsQfpABw3rmlK+W6cmXJxeA6g/mp2U8E5Ajgw7DP6Bh3nL0dZkPZJrDuCxhbDbZ+D7HRVperMgBHx/AXAZ3jn3cGFjr4eUqpe5T1Kcv0xtP5NOhTwq+H89K6f/Fp4RJc7RICfmVgyXvwzeOwb7Eu1aAeyNHAHwk8KyKHgGfjtxGRgiKy5O9GIjIb2ASUEZFwEenm4HGVcilu4kZwyWBCWobQvkx7/nPwPzTfPIR5Qd2IazcLROCnV2BqEwjXYVCVOP3ilVIZ0IFLBxi+ZTh/nP+DynkrM6hmfyocD4U1I+BmBFRoBQ0/BJ/iVpeq0liqffFKKWWNMj5lmNZoGsPrDefUjVO8tLQDQ2NOc7XX79CgHxxYCuNqwrJBEKlXRCs7DXylMigRofljzQlpGcIr5V7hl0O/0GxJe34uXIa4t8KgSjvY/I19Df4NYyH6ttUlK4vpkI5SmUTCYZ6KvhUZXGcwFWMFVnwIh1dCrgD7ME/F1uCm53qZlQ7pKOUC/h7mGVF/BGcjz/Lyry/zydGfudJmMnRcAN65YF53mPQ0HFtndbnKAhr4SmUiIkKzEs0IaRFCh/IdmH9oPs0WNGNuTASx3VdDi2/hxnmY3gx+bAcRB6wuWaUhHdJRKhM7dPkQw7YMY9u5bVTwrcDg2oOplLukfWx/3f9BdCRU7wRPDoQc+awuVzmBQ0srWEkDXynHGWNYcmwJX4Z9yYVbF2hVqhV9q/clT2ws/P4ZhE0BWxb7Mg11e4NnNqtLVg7QwFdKcePODSbsmMCsfbPI5pGNvtX70rpUa2yXjsGqj2FfCGTPD08NgmodwM1mdckqBXTSVilFds/svF/zfX5u/jNlfMowdPNQXvr1JXaYm9BuJry6HHIXgZA+MCEIDv5Xl2rIZDTwlXIxJfOUZPJzkxnVYBQXb12kw5IOfLjhQy75l4ZuK6DtdIi5DT+2hR9egNN/Wl2ychId0lHKhd2Mvsm3O75l5t6ZeHt406daH9qWbostLtY+tv/7Z3DrElRuB09/YP8NQKVrOoavlHqgI1eOMGLLCLac3UI5n3IMqj2Iqv5V4dYVWP8VbP7W3rDO61D/XfDKZWW56gE08JVSD2WMYfnx5Xwe+jnnI8/TomQL3q7+Nr7evnDlBPz2Kez8Cbx94In+EPgquHtaXba6hwa+UirZIqMj+Xbnt8zYMwNvD296V+3Ni2VexN3N3T6ev+IDOLYWfEpAw4+gfLB9eWaVLmjgK6Ue2dGrRxmxZQSbz2ymTJ4yDK4zmGr+1exX7hxaYV+jJ2IfFK4Fz30KAbWtLlmhga+USiFjDCuOr2BU6CjORZ7jhcde4J0a75DXOy/ExsCfs2D1cLhxFso1h2c+Ad/HrC7bpWngK6UcEhkdycSdE5m+dzreNm/erPYm7cq0sw/z3LkJG8fBhjEQG2Uf23+iP2TLa3XZLkkDXynlFMeuHmPk1pFsPL2R0nlKM6j2IGrkq2Hfef2c/Y5bf0wHz+xQ7x37VT0e3tYW7WI08JVSTmOMYdWJVXwW+hlnb56leYnmvBv4rn2YB+D8flj5ERxcBjkLw9ND7Nfx6xr8aSLVllYQER8RWSEih+J/5kmkTRERWS0i+0Rkj4j0deSYSilriQjPFH2GhcEL6VGpB8v+Wkbz+c2ZsXcGMXEx4F8WXv4JOi+2D+sseA0mNoAjq60u3eU5+k/uAGCVMaYUsCp++14xwL+MMeWAOsCbIlLeweMqpSyW1SMrfar3Yd4L86jiV4VRoaN4cfGLhJ2N/628eH3osRpaTYJbV2FGC5jZGs7tsbRuV+bQkI6IHACeNMacEZECwBpjTJmHvGchMM4Ys+Jhn69DOkplDMYYfjvxG5+FfsaZm2doWqIp/6rxL/yy+tkbRN+GrRNh3RcQdR2qvgxPDYacBa0tPBNKtTF8EblijMmdYPuyMea+YZ0E+4sBa4GKxphrSbTpCfQECAgIqHH8+PEU16eUSlu3Ym4xadckpu6eiqfNkzeqvMFL5V7Cw83D3iDyEqz9wh7+bu729feD+kKWHNYWnok4FPgishLIn8iuwcD05Aa+iGQHfgeGGWPmJadwPcNXKmM6fu04I7eOZP2p9ZTMXZJBtQdRM3/NfxpcOgar/g175kE2P/sdt6p3Bpu7dUVnEql5hp+sIR0R8QAWA8uNMV8l9/M18JXKuIwxrD65mlGhozh14xSNizfmvcD38M/q/0+j8DD47xA4sQnylrZ/catMY12qwQGpeQOURUDn+OedgYWJHFyAycC+Rwl7pVTGJiI8HfA0C4IX8FqV11h1fBXN5zdn+p7pRMdF2xsVDoSuS6HdLDBxMOclmNYUTm2ztvhMytEzfF9gLhAAnADaGmMuiUhBYJIxpomI1APWAbuAuPi3DjLGLHnY5+sZvlKZx8lrJxkZOpK14Wt5LNdjDKo9iFoFav3TIDYatk2DNSMh8gJUbA0NP4Q8xawqOUPSL14ppdKNNSfXMHLrSE7dOEWjYo14L/A98mXL90+D29fsyzRsGg8mFmr1hPr/gqw+ltWckWjgK6XSldsxt5m6eyqTdk3C5mbj9Sqv06FcBzxsHv80unrKvjDbn7PsN1xp8J49/N2zWFd4BqCBr5RKl05eP8moraNYE76G4rmKM6j2IOoUqHN3o7O77UsxH1kFuQPsa/BXaKVLNSQhNSdtlVIqxYrkKMLXDb9mfMPxRMdG0+O/PfjXmn9x9ubZfxrlrwgd50GHeZAlJ/zSDSY1hL82WFd4BqVn+EqpdCEqNoopu6cweddk3MSNXpV70al8p7uHeeJiYccc++0Wr5+GMk3sl3L6lbau8HRGh3SUUhlG+PVwRoWOYvXJ1RTLWYyBtQdSt2DduxvdiYTN38D60RAdCTU627+8ld0/0c90JRr4SqkMZ234WkZuHcnJ6yd5tuiz9KvZj/zZ7vnS/40I+P0zCJtiX3c/qC88/iZ4ZrOm6HRAA18plSFFxUYxbfc0Ju2ahIjQs3JPOpXvhKfN8+6GFw7Byo9h/2LIUQCeGgRVXwE3myV1W0kDXymVoZ26cYpRW0fx28nf7MM8tQZSt1Dd+xse32RfquFUGPiXh2f/DSWfcamlGvQqHaVUhlYoeyHGPD2GCc9MIM7E0WtlL95Z/Q5nbpy5u2HRx6H7Smgz1T62P6uNfR3+MzstqTu90TN8pVSGcif2DtP3TGfizokA9Kjcgy4Vutw/zBMTBaGTYe0ouHUFqrS3324xV+G0LzoN6ZCOUirTOX3jNJ+Hfs7KEysJyBHAwNoDqVeo3v0Nb12GdV/Blu/sQzt1XrffYN0rV9oXnQY08JVSmdbGUxsZsXUEf137i6eLPE2/Wv0olL3Q/Q2vnIBVQ2HXXMjqC0/0hxpdwd3z/rYZmAa+UipTuxN7hx/2/sDEnROJM3F0r9SdrhW7ksWWyLo7p/6wL9Xw1zrwKQHPfAzlXsg0E7s6aauUytQ8bZ50r9SdhcELaVC4AeP/HE/LhS1ZG772/saFqkPnEHh5Lrh5wNxOMOV5OLk17QtPYxr4SqlMo0D2Anz15Fd89+x32MTGm6ve5K3f3iL8evjdDUWg9PPw+kZoNtp+y8XJz9rD/+IRS2pPCzqko5TKlKJjo5mxbwbf7viWOBNHt4rd6FqxK17uXvc3jroBG7+GjWPtN2Kp2Q0a9INsvmlfuIN0DF8p5bLO3jzLF2FfsPyv5RTOXpgBtQbwRJEnEm98/ax9Df7tM8AzO9R/F2q/Zl+2IYPQwFdKubzNZzYzYssIjl49yhOFn6B/rf4UyVEk8cbn98GKj+DQcshZGBp+AJVezBBr8GvgK6UU9mGemftmMmHHBGLjYulWqRuvVnw18WEegKO/w4oP4MwOyF8ZnhsKJZ5M05ofVapdpSMiPiKyQkQOxf/Mk0gbLxHZKiI7RGSPiHziyDGVUiqlPGwedK3YlZAWITQMaMiEHRNosbAFa06uSfwNJZ6AHmug1ff2L3D9EAwz28C5vWlYtfM4+vvJAGCVMaYUsCp++15RwNPGmCpAVaCRiNRJpJ1SSqWJfNnyMeqJUUx+bjJeNi/e+u0t3lz1Jievnby/sZsbVH4ReofZb7Zycit8GwSL3rKP+WcgDg3piMgB4EljzBkRKQCsMcaUeUD7rMB64HVjzJaHfb4O6SilUlt0XDQ/7vuRb/78hpi4GLpW7Eq3St3wdk9iojbyEvw+CkIngc0D6r4FdftAluxpW3gSUm0MX0SuGGNyJ9i+bIxJbFjHBmwDSgLjjTH9H/CZPYGeAAEBATWOHz+e4vqUUiq5zkee58uwL1lybAkFsxWkf63+PFXkKSSpb+BeOgorP4G9CyCbPzw1EKp1Apt7mtZ9L4cCX0RWAvkT2TUYmJ6cwE+wPzcwH3jLGLP7YYXrGb5SKq2Fng1l+JbhHL5ymHqF6jGw1kACcgYk/YaTofY1+E9uhrxl4NlPoHQjy5ZqSM0z/Eca0ol/z0fATWPMFw/7fA18pZQVouOimb1vNt/s+IY7sXfoUqELPSr3SHqYxxj73bZWfASXjkDRevYregpVT9vCSd21dBYBneOfdwYWJnJwv/gze0TEG3gG2O/gcZVSKtV4uHnQqUInQlqE8Hyx5/l+1/cELwhm5fGVJHqSLALlmsObW6DJFxCxD75/Cn7uBpfTz7C0o2f4vsBcIAA4AbQ1xlwSkYLAJGNMExGpDEwHbNj/gZlrjPl3cj5fz/CVUulB2Nkwhm8dzqHLhwgqGMSAWgMolqtY0m+4fRXWj4bN34CJg9q9oP6/wDvJEW+n0S9eKaWUg2LiYpizfw7j/xxPVGwUnSt0pkelHmT1yJr0m66Gw2/DYMds+w1XnugHNbuDeyLLNjuJBr5SSjnJhVsX+CrsK0KOhpA/W3761ezHMwHPJH01D9jvqbviQzi6GnIXhWc+ggqtUmViV9fDV0opJ8nrnZfh9YczvdF0cnrm5N0179JrRS+OXT2W9JsKVIZOC6DDL/ZF2X5+FSY1hOMb06xu0DN8pZRKsZi4GH468BPjto/jduxtOpXvRK/KvR48zBMXax/i+e1TuH4Gyjaz33Urbymn1KRn+EoplQrc3dx5pdwrhLQMoUnxJkzZPYUXFrzA8r+WJ341D4CbDap1gLf+gKeHwNE18McPaVKvnuErpZSTbD+/neFbhrP/0n5qF6jNoNqDKJGrxIPfdOO8fRLXK5dTatAzfKWUSgPV/Ksxu+lsBtYayN4Le2m9qDVfbfuKyOjIpN+U3d9pYf8wGvhKKeVE7m7uvFzuZUJahtCsRDOm7p5K8wXNWXZsWdLDPGlEA18ppVKBr7cvQ4OGMqPxDHy9fHl/7fv0+G8Pjlyx7ibpGvhKKZWKqvpXZXbT2QyuPZi9l/bSZlEbvgz7kpvRN9O8Fg18pZRKZTY3G+3Ltmdxy8W8UPIFpu2ZxgvzX2DJ0SVpOsyjga+UUmnEx8uHT+p+wswmM/H19qX/uv50+283Dl8+nCbH18BXSqk0VsWvCrObzuaDOh9w4NIB2oa05fPQz7lx50aqHlcDXymlLGBzs/FimRdZ3HIxwSWDmbF3Bi8seIHFRxen2jCPBr5SSlkoj1cePq77MbOazMI/qz8D1w2k6/Ku3Iq55fRjWXvzRaWUUgBU8qvErCazmHd4Hrsv7E767loO0MBXSql0wuZmo23ptrQt3TZVPl+HdJRSykVo4CullIvQwFdKKRfhUOCLiI+IrBCRQ/E/k7xDr4jYRGS7iCx25JhKKaVSxtEz/AHAKmNMKWBV/HZS+gL7HDyeUkqpFHI08IOB6fHPpwMtEmskIoWBpsAkB4+nlFIqhRwN/HzGmDMA8T/9k2g3GugHxD3sA0Wkp4iEiUhYRESEg+UppZT620OvwxeRlUD+RHYNTs4BRKQZcN4Ys01EnnxYe2PMRGAi2G9xmJxjKKWUejiH7mkrIgeAJ40xZ0SkALDGGFPmnjYjgI5ADOAF5ATmGWM6JOPzI4DjKS7QenmBC1YX4aDM0AfIHP3IDH0A7UdqK2qM8Utsh6OB/zlw0RgzUkQGAD7GmH4PaP8k8J4xplmKD5qBiEhYUjcTzigyQx8gc/QjM/QBtB9WcnQMfyTwrIgcAp6N30ZECorIEkeLU0op5TwOraVjjLkINEzk9dNAk0ReXwOsceSYSimlUka/aZu6JlpdgBNkhj5A5uhHZugDaD8s49AYvlJKqYxDz/CVUspFaOArpZSL0MB3ouQsJiciRURktYjsE5E9ItLXilqTktwF8URkioicF5HdaV1jUkSkkYgcEJHD8ZcJ37tfRGRs/P6dIlLdijofJhn9KCsim0QkSkTes6LG5EhGP16J/3PYKSIbRaSKFXU+SDL6EBxf/5/xKwTUs6LOZDPG6MNJD2AUMCD++QDgs0TaFACqxz/PARwEyltd+6P0IX5fA6A6sNvqmuPrsQFHgBKAJ7Dj3v+u2K8cWwoIUAfYYnXdKeyHP1ATGIb9ey2W153CftQF8sQ/b5ze/jyS2Yfs/DMXWhnYb3XdD3roGb5zPXQxOWPMGWPMH/HPr2NfQbRQWhWYDMlaEM8Ysxa4lEY1JUct4LAx5qgx5g4wB3tfEgoGfjB2m4Hc8d8QT08e2g9jzHljTCgQbUWByZScfmw0xlyO39wMFE7jGh8mOX24YeLTHsgGpOurYDTwnSu5i8kBICLFgGrAltQvLdkeqQ/pSCHgZILtcO7/hzQ5bayWEWpMjkftRzfsv32lJ8nqg4i0FJH9wK/Aq2lUW4roTcwfkaOLySX4nOzAL8DbxphrzqjtEY7tlD6kM5LIa/eebSWnjdUyQo3Jkex+iMhT2AM/vY1/J6sPxpj5wHwRaQAMBZ5J7cJSSgP/ERljkvzDFJFzIlLA/LOY3Pkk2nlgD/tZxph5qVRqkpzRh3QoHCiSYLswcDoFbayWEWpMjmT1Q0QqY79PRmNj/+Z+evJIfxbGmLUi8piI5DXGpMdF1XRIx8kWAZ3jn3cGFt7bQEQEmAzsM8Z8lYa1JddD+5BOhQKlRKS4iHgC7bH3JaFFQKf4q3XqAFf/Hr5KR5LTj4zgof0QkQBgHtDRGHPQghofJjl9KBn/d5r4q748gfT2D9c/rJ41zkwPwBf7rR4Pxf/0iX+9ILAk/nk97L8W7gT+jH80sbr2R+lD/PZs4Az2icNwoFs6qL0J9quejgCD4197DXgt/rkA4+P37wICra45hf3IH//f/BpwJf55TqvrTkE/JgGXE/w9CLO65hT0oT+wJ77+TUA9q2t+0EOXVlBKKRehQzpKKeUiNPCVUspFaOArpZSL0MBXSikXoYGvlFIuQgNfKaVchAa+Ukq5iP8Hv0gs+D0pFUYAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "for norm in plane_norms:\n",
    "    plt.plot([0, norm[0]], [0, norm[1]])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Looks good, now we will create a set of vectors to convert into binary format - these are `a`, `b`, and `c`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = np.asarray([1, 2])\n",
    "b = np.asarray([2, 1])\n",
    "c = np.asarray([3, 1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Calculate the dot-product between each and the normal vectors we built before."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0.41487151, -0.32851916, -0.39600301, -0.16017455])"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# calculate the dot product for each of these\n",
    "a_dot = np.dot(a, plane_norms.T)\n",
    "b_dot = np.dot(b, plane_norms.T)\n",
    "c_dot = np.dot(c, plane_norms.T)\n",
    "a_dot"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And use the dot-product to create a Boolean array, we convert these values to ints - creating our hash buckets/binary vectors."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ True, False, False, False])"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# we know that a positive dot product == +ve side of hyperplane\n",
    "# and negative dot product == -ve side of hyperplane\n",
    "a_dot = a_dot > 0\n",
    "b_dot = b_dot > 0\n",
    "c_dot = c_dot > 0\n",
    "a_dot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1, 0, 0, 0])"
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# finally, we convert our boolean arrays to int arrays to make bucketing\n",
    "# easier (although we can still use boolean values for Hamming distance)\n",
    "a_dot = a_dot.astype(int)\n",
    "b_dot = b_dot.astype(int)\n",
    "c_dot = c_dot.astype(int)\n",
    "a_dot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 1, 1, 0])"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "b_dot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 1, 1, 0])"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c_dot"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And now we bucket each of our vectors, `a`, `b`, and `c` will be represented by `0`, `1`, and `2` respectively."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'1000': [0], '0110': [1, 2]}\n"
     ]
    }
   ],
   "source": [
    "vectors = [a_dot, b_dot, c_dot]\n",
    "buckets = {}\n",
    "i = 0\n",
    "\n",
    "for i in range(len(vectors)):\n",
    "    # convert from array to string\n",
    "    hash_str = ''.join(vectors[i].astype(str))\n",
    "    # create bucket if it doesn't exist\n",
    "    if hash_str not in buckets.keys():\n",
    "        buckets[hash_str] = []\n",
    "    # add vector position to bucket\n",
    "    buckets[hash_str].append(i)\n",
    "\n",
    "print(buckets)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.13 (default, Mar 28 2022, 06:59:08) [MSC v.1916 64 bit (AMD64)]"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "5fe10bf018ef3e697f9035d60bf60847932a12bface18908407fd371fe880db9"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
